(一) 基本数据类型和引用数据类型的区别

  1. 基本数据类型存放在栈区
  2. 引用数据类型存放在堆区(它的引用存放在栈区)

以下是基本数据类型和引用数据类型的存放方式

image.png

练习: 说出下面代码运行的结果, 并说说为什么

var a = 100;
var b = a;
b = 200;
console.log(a); // a的值是: 100

var obj1 = {
	name: '张三',
  age: 100
}
var obj2 = obj1;
obj2.name = '李四';

console.log(obj1.name);  // obj1.name的值是多少 李四

标题

js垃圾回收机制和内存泄漏

(二) 函数的调用方式

  1. 普通调用
  2. 回调
  3. 递归调用
  4. 自调用
// 回调函数
function test1(callback) {
	callback(22222);
}
function test2(arg) {
    console.log(arg);
}
// test作为参数传入test1,这时test2被称为回调函数
test1(test2);

$.ajax({
    type:'get',
    data: {},
    url:'',
    dataType: 'json',
    // 成功时的回调函数
    success: function(res){},
    // 失败时的回调函数
    error: function(error){}
});


// 递归就是函数自己调用自己
function add(num) {
   if (num<10) { 
        console.log(num);
       // 函数递归
        add(++num);
   } 
}
add(1);

// 适用函数递归进行数字累加
var sum = 0;
function add(min,max) {
   sum += min;  
    min++;
    console.log(sum);
    if (min<=max) {
        add(min,max);
    }  
}

// 自调用:可以解决全局变量污染
(function(){
	var a = 100;
    var b = 200;
    console.log(a);
    console.log(b);    
})();

(三) js垃圾回收机制

垃圾回收机制是什么

垃圾回收机制(GC:Garbage Collection),执行环境负责管理代码执行过程中使用的内存。垃圾收集器会定期(周期性)找出那些不在继续使用的变量,然后释放其内存。但是这个过程不是实时的,因为其开销比较大,所以垃圾回收器会按照固定的时间间隔周期性的执行。

垃圾回收策略是什么

2种最为常用:标记清除和引用计数,其中标记清除更为常用。

标记清除(mark-and-sweep):是对于脱离作用域的变量进行回收,当进入作用域时,进行标记,离开作用域时,标记并回收这些变量。到目前为止,IE、Firefox、Opera、Chrome、Safari的js实现使用的都是标记清除的垃圾回收策略或类似的策略,只不过垃圾收集的时间间隔互不相同。 当变量进入环境(例如,在函数中声明一个变量)时,就将这个变量标记为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占的内存,因为只要执行流进入相应的环境,就可能用到它们。而当变量离开环境时,这将其 标记为“离开环境”。

引用计数:引用计数是跟踪记录每个值被引用的次数。就是变量的引用次数,被引用一次则加1,当这个引用计数为0时,被视为准备回收的对象,每当过一段时间开始垃圾回收的时候,就把被引用数为0的变量回收。引用计数方法可能导致循环引用,类似死锁,导致内存泄露。

如何减少垃圾回收开销

由于每次的垃圾回收开销都相对较大,并且由于机制的一些不完善的地方,可能会导致内存泄露,我们可以利用一些方法减少垃圾回收,并且尽量避免循环引用。

  1. 在对象结束使用后 ,令obj = null。这样利于解除循环引用,使得无用变量及时被回收;

  2. js中开辟空间的操作有new(), [ ], { }, function (){..}。尽量减少此类操作, 最大限度的实现对象的重用;举例:

var arr = [1,2,3];
// 清空数组
arr = [];  // 此方法会再创建一个新的数组,在内存中开辟一块新的空间, 是个不好的做法
arr.length = 0;  // 好的做法,能重用原数组
  1. 慎用闭包。闭包容易引起内存泄露。本来在函数返回之后,之前的空间都会被回收。但是由于闭包可能保存着函数内部变量的引用,且闭包在外部环境,就会导致函数内部的变量不能够销毁。

如何优化垃圾回收

分代回收(Generation GC):与Java回收策略思想是一致的。目的是通过区分“临时”与“持久”对象;多回收“临时对象”区(young generation),少回收“持久对象”区(tenured generation),减少每次需遍历的对象,从而减少每次GC的耗时。 增量GC:这个方案的思想很简单,就是“每次处理一点,下次再处理一点,如此类推。

(四) 常见内存泄露的原因

  • 全局变量引起的内存泄露
  • 闭包引起的内存泄露:慎用闭包
  • dom清空或删除时,事件未清除导致的内存泄漏
  • 循环引用带来的内存泄露

参考连接: https://blog.csdn.net/qq_21428081/article/details/82465801https://www.cnblogs.com/kaicy/p/14715750.html

Invalid code snippet option

js异步编程

=======

(五) js异步编程

492dc9a627cd12d765012624d9b582bae76943be

(1) 同步和异步(理解)

js中的同步和异步跟生活中的同步异步意思有些不太一样

js的同步指的是,同一时间只执行一个任务, 当一个任务结束以后才能执行下一个任务。

js的异步指的是,同一时间可以执行多个任务,常见的js异步操作有setTimeout(), setInterval(), ajax请求等。

(2) 单线程和多线程(理解)

单线程: 同一时间只能做一件事 多线程: 同一时间可以做多件事 JavaScript语言的一大特点就是单线程, 那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊。比如java和c#就是多线程的语言。 JavaScript的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准? 所以,为了避免复杂性,从一诞生,JavaScript就是单线程,这已经成了这门语言的核心特征,将来也不会改变。

(3) JS中的异步运行机制

也称js事件循环机制(Event Loop) JS是单线程的,那么他是如何是实现异步操作的? JS中的异步运行机制(背诵):

(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。

(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件(事件一般都有对应的回调函数)。

(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。

(4)主线程不断重复上面的前三步。

举例说明:

<script>
    setTimeout(function(){
        console.log('时间到了');
    }, 2000);
    window.onload = function() {
        console.log('页面加载完毕');
    }
     console.log('哈哈哈');
     console.log('嘿嘿嘿');
     console.log('呵呵呵');
</script>

image-20220205222820050

思考问题:

  1. 如果把例子中的setTimeout 2000改为0, 执行结果是会不会发生改变

    答: 不会, 因为setTimeout是异步任务, 所以要等到同步任务执行完毕才开始执行

  2. 若setTimeout的延迟设为1秒, 1秒后setTimeout的回调函数一定会执行吗

    答: 不一定, 取决于有没有同步任务, 以及同步任务执行需要的时间\

    <script>
        // 定义代码执行开始时间
        var time1 = new Date();
        setTimeout(function() {
            var time2 = new Date();
            console.log('回调执行时间:',time2-time1);
            console.log('执行延迟任务');
        }, 1000);
    
        var count = 0;
        for(;;) {
            count++;
            if(count === 1000000000) {
                break;
            }
        } 
    </script>
    
  3. 说出以下代码运行的结果, 并解释为什么

    • 循环5次(同步), 先执行, 执行完以后, i的值5
    • 5个setTimeout(异步), 等同步任务执行完才开始执行, 开始执行的时候, i的值已经变为5
  4.  for (var i = 0; i < 5; i++) {
            setTimeout(function () {
                console.log(i);
            }, 0)
        }
    
  5. 参考链接: https://www.ruanyifeng.com/blog/2014/10/event-loop.html

(六) this的指向

问题:谈谈this的指向(背诵) 或问:解释下JavaScript中this是如何工作

答: this的指向有以下几种情况

  1. this永远指向函数运行时所在的对象,而不是函数被创建时所在的对象。
  2. 普通的函数调用: 函数被谁调用,this就是谁。
  3. 匿名函数或不处于任何对象中的函数指向window 。
  4. 如果是call, apply, bind,this指向了这三者调用函数时传入的第一个参数
  5. 箭头函数的指向: 由于箭头函数不绑定this, 它会捕获其所在(即定义的位置)上下文的this值, 作为自己的this值

(1) 普通的函数调用

函数被谁调用,this就是谁。

<script>
    // 例子1
    function test() {
        console.log(this); // this指向window,是因为window在调用test
    }

    window.test(); 


    // 例子2
    var obj = {
        name: '张三',
        say() {
            console.log(this.name);   // obj.say()结果是张三  fn()调用结果是
        }
    }
    obj.say(); // this指向obj

    // 将obj.say赋给了一个新的变量
    var fn = obj.say;
    window.fn();  // this指向window 
</script>

(2) 匿名函数或不处于任何对象中的函数指向window

<script> 
    var obj = {
        say: function() {
            setTimeout(function() {
                console.log(this);  // this指向了window
            },1000);
        }
    }
    obj.say(); 
</script>

(3) call, apply, bind的指向

如果是call, apply, bind,this指向了这三者调用函数时传入的第一个参数, 详看 call, apply, bind下一个知识点: call, apply和bind的区别

(4) 构造函数的this指向

构造函数的话,如果不用new操作符而直接调用,那即this指向window。用new操作符生成对象实例后,this就指向了新生成的对象。

(5) 箭头函数的this

由于箭头函数不绑定this, 它会捕获其所在(即定义的位置)上下文的this值, 作为自己的this值: 将在es6新特性里细讲

(七) call, apply和bind的区别

问题: call, apply和bind有什么区别

  1. 三者都会改变this的指向, 区别:

  2. call 和 apply 的功能相同,区别在于传参的方式不一样:

    • call的第一个参数是this要指向的对象, 第二个,第三个....都是调用函数需要的参数。
    • apply第一个参数是this要指向的对象, 第二个参数是数组或类数组, 数组是放的是调用函数需要的参数。
// 1. 改变this的指向
<script>
    var username = '张三';
    var obj1 = {
        username: '李四'
    }   
    var obj2 = {
        username: '王五'
    }   
    function say() {
        console.log(this,this.username);
    } 
    say();  // this指向谁window
		// 使用call和apply时,this指向了传入的对象
    say.call(obj1);  // this指向obj1,打印的结果李四
    say.apply(obj2); // this指向obj2,打印的结果王五
</script>

// 2.call和apply传参
<script>
    var username = '张三';
    var age = 18;
    var obj1 = {
        username: '李四'
    }
    var obj2 = {
        username: '王五'
    } 
    // 需要两个参数:省份和城市
    function say(provice,city) {
        var str = '我叫'+this.username+',我来自'+provice+city;
        console.log(str);
    }
    say('广东','深圳');

    // call传参: 第一个参数,是this指向的对象,第二个,第三个.....,都是调用函数需要参数
    say.call(obj1,'湖南','长沙');
    // apply传参: 第一个参数是this要指向的对象,第二个参数是个数组,数组是调用函数需要的参数
    say.apply(obj2,['河南','驻马店']); 
</script>
  1. bind 和 call/apply 有一个很重要的区别,

    • 一个函数被 call/apply 的时候,会直接调用,但是 bind 会创建一个新函数, 不会直接调用
    • 当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this, 新函数的调用和原来函数的调用一模一样,除了this的指向不一样之外
<script>
    var username = '张三';
    var age = 18;
    var obj = {
        username: '李四'
    } 
    // 需要两个参数:省份和城市
    function say(provice,city) {
        var str = '我叫'+this.username+',我来自'+provice+city;
        console.log(str);
    } 
    // 得到一个新的函数,函数的this指向了传入的对象
    var say2 = say.bind(obj);
    say2('四川','成都'); 
</script>

(八) 闭包及其作用

(一) 闭包是什么, 闭包形成的原因(背诵)

**闭包的概念:**闭包是指有权访问另一个函数作用域中的变量的函数,创建闭包最常用的方式就是在一个函数内部创建另一个函数(函数中的函数)。

简单概括: 闭包就是一个能访问另外一个函数内部变量的函数

**闭包形成的原因:**函数内部的局部变量一般在函数运行结束的时候就会被销毁, 但如果局部变量因为被外部引用而导致没有被销毁, 就形成了闭包

闭包的特性:

闭包可以让我们在函数外部访问函数内部的变量

<script>
    function test() {
        var color = 'red';
        // demo函数是个闭包
        function demo() {
            console.log(color);
        } 
        return demo; 
    }
    // 调用test得到一个函数,使用变量fn存储起来
    var fn = test(); 

    // 调用fn函数
    fn();  // 调用结果:打印"red" 
  
  	test()();
</script>

(二) 闭包有哪些作用(背诵)

或问: 闭包有哪些应用场景

  1. 封装私有变量(私有属性)
  2. 模仿块级作用域(ES5中没有块级作用域)
  3. 实现JS的模块
1.封装私有变量(私有属性)

私有变量(私有属性)是后台语言经常用的东西, 意思是这个属性是私有的,不能随意更改,要改,必须通过指定的set和get方法进行获取和修改

<script>
function getCatName() {
    var _catName = '小花';
    // 获取_catName
    function get() {
        return _catName;
    }
    // 修改_catName,需要传入新名字
    function set(newName) {
        _catName = newName
    }
    return {
        get:get,
        set:set
    }
}

// 调用getCatName(),得到一个对象, 对象包含了get方法和set方法
var obj = getCatName();

// 调用get方法获取catName
var catName = obj.get();
console.log('catName',catName);

// 调用set方法来修改catName
obj.set('小黑');
var catName = obj.get();
console.log('catName',catName); 
</script>
2.模仿块级作用域

ES5中没有块级作用域

  1. js没有块级作用域
  2. 函数自调用
// 1.js没有块级作用域
<script>
    {
        var a = 2;
    } 
    console.log(a); 

    for(var i=0;i<5;i++) {

    }
    console.log(i);
</script>

// 2.函数自调用
<script>
    (function() {
        console.log('我跟自己玩');
    })(); 
</script>

// 3.说出以下代码运行的结果
<script>
for(var i=0;i<5;i++) {
	setTimeout(function(){
  	console.log(i);   // 打印5个5
  },i*1000);
}  
</script>

// 4.使用闭包模拟块级作用
// 因为使用了闭包,循环了五次,就产生了五个闭包,每个闭包都有独立的内部变量j,j的值是0,1,2,3,4
<script>
    for (var i = 0; i < 5; i++) {
        (function (j) {
            setTimeout(function () {
                console.log(j);
            }, j * 1000);
        })(i);
    }  
</script>

// es6的let声明的变量是存在块级作用域
<script>
    for (let i = 0; i < 5; i++) { 
      setTimeout(function () {
        console.log(j);
      }, j * 1000); 
    }  
</script>
3.实现JS的模块(js库)
(function () {
    function get() {
        var xhr = new XMLHttpRequest();
        // xhr.opon('');
        // ......
        console.log('这是get方法');
    }
    function post() {
        var xhr = new XMLHttpRequest();
        // xhr.opon('');
        // ......
        console.log('这是post方法');
    }
 
    var axios = {
        get: get,
        post: post
    } 
    window.axios = axios; 
})();
使用闭包的注意点

由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题

(九) 深拷贝和浅拷贝

概念和定义

定义: 深拷贝和浅拷贝最根本的区别在于是否真正获取一个对象的复制实体,而不是引用。

**浅拷贝:**仅仅是指向被复制的内存地址,如果原地址发生改变,那么浅复制出来的对象也会相应的改变。

**深拷贝:**在计算机中开辟一块新的内存地址用于存放复制的对象。

TIP

浅拷贝

var obj = {
    name:'wsscat',
    age:0
}
var obj2 = obj;
obj2['c'] = 5;
console.log(obj);//Object {name: "wsscat", age: 0, c: 5}
console.log(obj2);////Object {name: "wsscat", age: 0, c: 5}

var arr1 = [1,2,3,4];
var arr2 = arr1;
arr2.push(5);
console.log(arr1); // [1,2,3,4,5]
console.log(arr2); // [1,2,3,4,5]

TIP

只拷贝第一层

1. 深拷贝方法 for in 的方法只能拷贝第一层

2. 使用es6的扩展运算符得到的对象也是只拷贝第一层

TIP

完全拷贝

3. 使用JSON的提供api

var obj = {
  a:2,
  b:3,
  o: {
    x: 100
  }
}

var objStr = JSON.stringify(obj);
var obj2 = JSON.parse(objStr);
obj2.o.x = 1000;
consolo.log(obj2.o.x); // 1000
consolo.log(obj.o.x); // 100

4. 函数递归调用

var obj = {
    name: 'wsscat',
    age: 0,
	hobby:['篮球','羽毛球','乒乓球']
}
var deepCopy = function(source) {
    var result =  Array.isArray(source)?[]:{};
    for(var key in source) {
        if(typeof source[key] === 'object') {
            result[key] = deepCopy(source[key])
        } else {
            result[key] = source[key]
        }
    }
    return result;
}
var newObj = deepCopy(obj) 
console.log(obj.hobby === newObj.hobby);   

其它方法:

https://www.jianshu.com/p/1c142ec2ca45

(十) 严格模式'use strict'

在代码中出现表达式-"use strict"; 意味着代码按照严格模式解析,这种模式使得Javascript在更严格的条件下运行。

好处:

  1. 消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为;
  2. 消除代码运行的一些不安全之处,保证代码运行的安全;
  3. 提高编译器效率,增加运行速度;
  4. 为未来新版本的Javascript做好铺垫。

坏处:

  1. 同样的代码,在"严格模式"中,可能会有不一样的运行结果;

    "use strict";
    
    function add() {
      console.log(this);  // undefined
    }
    add();
    
    // "use strict";
    function say() {
        a = 100;
    }
    
  2. 一些在"正常模式"下可以运行的语句,在"严格模式"下将不能运行。

(十一) 防抖和节流

概念

在前端开发的过程中,我们经常会需要绑定一些持续触发的事件,如滚动、输入等等,但有些时候我们并不希望在事件持续触发的过程中那么频繁地去执行函数,防抖和节流是比较好的解决方案。

(1)所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。使用场景:注册的时候检查用户名是否已经被注册.

(2)所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数。节流会稀释函数的执行频率。使用场景:监听滚动条是否到了顶部或底部.

(一) 防抖例子

<!DOCTYPE html>
<head>
    <meta charset="UTF-8">
</head>
<body>
    用户名: <input placeholder="请输入用户名" id="username" />

    <script>
        // 1.定义一个timer定时器
        var timer;
        // 2.获取input节点
        var $username = document.getElementById('username');
        // 3.绑定input事件
        $username.addEventListener('input', function() {
            // 4.每次输入都会清除定时器,所以连续输入,定时器是不会触发,触发,除非停止输入超过1秒定时器才会执行
            clearTimeout(timer);
            timer = setTimeout(function() {
                console.log('发请求到后台,检查用户是否已注册');
            }, 1000)
        }, false)
    </script>
</body>

封装函数

// 上面例子中的函数,一般单独写出来,给它命名为debounce
<!DOCTYPE html> 
<head>
    <meta charset="UTF-8">
</head>

<body>
    用户名: <input placeholder="请输入用户名" id="username" />

    <script>
        // 1.定义一个timer定时器
        var timer;
        // 2.获取input节点
        var $username = document.getElementById('username');
        // 3.绑定input事件
        $username.addEventListener('input', debounce(1000), false)
 
        function debounce(count) {
            var timer;
            return function () {
                clearTimeout(timer);
                timer = setTimeout(function () {
                    console.log("发请求到后台,检查用户是否已注册");
                }, count);
            }
        }
    </script>
</body>

(二) 节流例子

<!DOCTYPE html>

<head>
    <meta charset="UTF-8">
    <style>
        p {height: 100px}
    </style>
</head>

<body>
    <div id="box">
        <p>ppppppppppppppppp</p>
        <p>ppppppppppppppppp</p>
        <p>ppppppppppppppppp</p>
        <p>ppppppppppppppppp</p>
        <p>ppppppppppppppppp</p>
        <p>ppppppppppppppppp</p>
        <p>ppppppppppppppppp</p>
        <p>ppppppppppppppppp</p>
        <p>ppppppppppppppppp</p>
        <p>ppppppppppppppppp</p>
        <p>ppppppppppppppppp</p>
        <p>ppppppppppppppppp</p>
        <p>ppppppppppppppppp</p>
        <p>ppppppppppppppppp</p>
        <p>ppppppppppppppppp</p>
        <p>ppppppppppppppppp</p>
        <p>ppppppppppppppppp</p>
        <p>ppppppppppppppppp</p>
        <p>ppppppppppppppppp</p>
        <p>ppppppppppppppppp</p>
        <p>ppppppppppppppppp</p>
        <p>ppppppppppppppppp</p>
        <p>ppppppppppppppppp</p>
        <p>ppppppppppppppppp</p>
        <p>ppppppppppppppppp</p>
        <p>ppppppppppppppppp</p>
        <p>ppppppppppppppppp</p>
        <p>ppppppppppppppppp</p>
    </div>

    <script>
        var $username = document.getElementById('username');
        window.onscroll = throttle;
        // 1.上一次事件触发的时间
        let oldTime = Date.now(); // 10:00:00
        function throttle() { 
            // 现在触发的时间
            let newTime = Date.now();
            // 现在点击的时间跟上一次点击的时间如果超过了一秒,执行回调
            if (newTime - oldTime >= 1000) {
                // 在这里执行我们想做的事情
                console.log('想干嘛干嘛');
                // 从新计时,这次点击的时间成为下一次点击时间的起点
                oldTime = newTime;
            }
        }
    </script>
</body>

(三) 防抖和节流的区别

函数防抖和节流区别在于,当事件持续被触发,如果触发时间间隔短于规定的等待时间(n秒),那么

  • 函数防抖的情况下,函数将一直推迟执行,造成不会被执行的效果;
  • 函数节流的情况下, 函数将按指定的时间间隔持续执行。

(十二) Eslint

http://docs.huruqing.cn/第12章 工具/VsCode.html#eslint配置

(1) Eslint常用命令

团队合作时, 统一代码规范

  1. 忽略 no-undef 检查 /* eslint-disable no-undef */

  2. 忽略 no-new 检查 /* eslint-disable no-new */

  3. 检查且修复 eslint * --fix

  4. 检查指定文件 eslint app.js --fix

  5. eslint初始化

    npm init -y
    npx eslint --init
    

(2) Eslint配置

  1. 安装eslint

    npm i eslint -g
    

    vscode搜索插件eslint安装

  2. .eslintrc.js配置,使用npx eslint --init创建基本配置, 得到以下配置

     module.exports = {
        "env": {
            "browser": true,
            "es2021": true
        },
        "extends": "eslint:recommended",
        "parserOptions": {
            "ecmaVersion": "latest",
            "sourceType": "module"
        },
        "rules": {
        }
    }
    
  3. setting.json配置

    {
        // 其它设置
        .....
        
        // eslint配置
        "eslint.alwaysShowStatus": true, // 显示eslint状态
        "eslint.format.enable": true,  // 使用eslint格式化代码
        "editor.codeActionsOnSave": {
            // 保存时自动修复所有错误
            "source.fixAll.eslint": true
        }
    }
    
  4. 使用eslint规则格式化代码

    • 文件 -> 首选项 -> 扩展 -> eslint

      • eslint > format:Enable 把eslint作为一个格式化工具
      • 右键-> 使用...格式化文档 -> 配置默认格式化程序 -> 选择eslint
      • Eslint: always show status 在任务栏显示eslint状态
    • 保存时自动修复问题

       "editor.codeActionsOnSave": {
          // 保存时自动修复所有错误
          "source.fixAll.eslint": true
        },
      
  5. 自定义规则

    "off"或者0,不启用这个规则

    "warn"或者1,出现问题会有警告

    "error"或者2,出现问题会报错

     'rules': {
            'no-alert': 0, //禁止使用alert confirm prompt
            'no-array-constructor': 2, //禁止使用数组构造器
            'no-bitwise': 0, //禁止使用按位运算符
            'no-caller': 1, //禁止使用arguments.caller或arguments.callee
            'no-catch-shadow': 2, //禁止catch子句参数与外部作用域变量同名
            'no-class-assign': 2, //禁止给类赋值
            'no-cond-assign': 2, //禁止在条件表达式中使用赋值语句
            'no-console': 0, //禁止使用console
            'no-const-assign': 2, //禁止修改const声明的变量
            'no-constant-condition': 2, //禁止在条件中使用常量表达式 if(true) if(1)
            'no-continue': 0, //禁止使用continue
            'no-control-regex': 2, //禁止在正则表达式中使用控制字符
            'no-debugger': 2, //禁止使用debugger
            'no-delete-var': 2, //不能对var声明的变量使用delete操作符
            'no-div-regex': 1, //不能使用看起来像除法的正则表达式/=foo/
            'no-dupe-keys': 2, //在创建对象字面量时不允许键重复 {a:1,a:1}
            'no-dupe-args': 2, //函数参数不能重复
            'no-duplicate-case': 2, //switch中的case标签不能重复
            'no-else-return': 2, //如果if语句里面有return,后面不能跟else语句
            'no-empty': 2, //块语句中的内容不能为空
            'no-empty-character-class': 2, //正则表达式中的[]内容不能为空
            'no-empty-label': 0, //禁止使用空label
            'no-eq-null': 2, //禁止对null使用==或!=运算符
            'no-eval': 1, //禁止使用eval
            'no-ex-assign': 2, //禁止给catch语句中的异常参数赋值
            'no-extend-native': 2, //禁止扩展native对象
            'no-extra-bind': 2, //禁止不必要的函数绑定
            'no-extra-boolean-cast': 2, //禁止不必要的bool转换
            'no-extra-parens': 2, //禁止非必要的括号
            'no-extra-semi': 2, //禁止多余的冒号
            'no-fallthrough': 1, //禁止switch穿透
            'no-floating-decimal': 2, //禁止省略浮点数中的0 .5 3.
            'no-func-assign': 2, //禁止重复的函数声明
            'no-implicit-coercion': 1, //禁止隐式转换
            'no-implied-eval': 2, //禁止使用隐式eval
            'no-inline-comments': 0, //禁止行内备注
            'no-inner-declarations': [2, 'functions'], //禁止在块语句中使用声明(变量或函数)
            'no-invalid-regexp': 2, //禁止无效的正则表达式
            'no-invalid-this': 2, //禁止无效的this,只能用在构造器,类,对象字面量
            'no-irregular-whitespace': 2, //不能有不规则的空格
            'no-iterator': 2, //禁止使用__iterator__ 属性
            'no-label-var': 2, //label名不能与var声明的变量名相同
            'no-labels': 2, //禁止标签声明
            'no-lone-blocks': 2, //禁止不必要的嵌套块
            'no-lonely-if': 2, //禁止else语句内只有if语句
            'no-loop-func': 1, //禁止在循环中使用函数(如果没有引用外部变量不形成闭包就可以)
            'no-mixed-requires': [0, false], //声明时不能混用声明类型
            'no-mixed-spaces-and-tabs': [2, false], //禁止混用tab和空格
            'linebreak-style': [0, 'windows'], //换行风格
            'no-multi-spaces': 1, //不能用多余的空格
            'no-multi-str': 2, //字符串不能用\换行
            'no-multiple-empty-lines': [1, { 'max': 2 }], //空行最多不能超过2行
            'no-native-reassign': 2, //不能重写native对象
            'no-negated-in-lhs': 2, //in 操作符的左边不能有!
            'no-nested-ternary': 0, //禁止使用嵌套的三目运算
            'no-new': 1, //禁止在使用new构造一个实例后不赋值
            'no-new-func': 1, //禁止使用new Function
            'no-new-object': 2, //禁止使用new Object()
            'no-new-require': 2, //禁止使用new require
            'no-new-wrappers': 2, //禁止使用new创建包装实例,new String new Boolean new Number
            'no-obj-calls': 2, //不能调用内置的全局对象,比如Math() JSON()
            'no-octal': 2, //禁止使用八进制数字
            'no-octal-escape': 2, //禁止使用八进制转义序列
            'no-param-reassign': 2, //禁止给参数重新赋值
            'no-path-concat': 0, //node中不能使用__dirname或__filename做路径拼接
            'no-plusplus': 0, //禁止使用++,--
            'no-process-env': 0, //禁止使用process.env
            'no-process-exit': 0, //禁止使用process.exit()
            'no-proto': 2, //禁止使用__proto__属性
            'no-redeclare': 2, //禁止重复声明变量
            'no-regex-spaces': 2, //禁止在正则表达式字面量中使用多个空格 /foo bar/
            'no-restricted-modules': 0, //如果禁用了指定模块,使用就会报错
            'no-return-assign': 1, //return 语句中不能有赋值表达式
            'no-script-url': 0, //禁止使用javascript:void(0)
            'no-self-compare': 2, //不能比较自身
            'no-sequences': 0, //禁止使用逗号运算符
            'no-shadow': 2, //外部作用域中的变量不能与它所包含的作用域中的变量或参数同名
            'no-shadow-restricted-names': 2, //严格模式中规定的限制标识符不能作为声明时的变量名使用
            'no-spaced-func': 2, //函数调用时 函数名与()之间不能有空格
            'no-sparse-arrays': 2, //禁止稀疏数组, [1,,2]
            'no-sync': 0, //nodejs 禁止同步方法
            'no-ternary': 0, //禁止使用三目运算符
            'no-trailing-spaces': 1, //一行结束后面不要有空格
            'no-this-before-super': 0, //在调用super()之前不能使用this或super
            'no-throw-literal': 2, //禁止抛出字面量错误 throw "error";
            'no-undef': 1, //不能有未定义的变量
            'no-undef-init': 2, //变量初始化时不能直接给它赋值为undefined
            'no-undefined': 2, //不能使用undefined
            'no-unexpected-multiline': 2, //避免多行表达式
            'no-underscore-dangle': 1, //标识符不能以_开头或结尾
            'no-unneeded-ternary': 2, //禁止不必要的嵌套 var isYes = answer === 1 ? true : false;
            'no-unreachable': 2, //不能有无法执行的代码
            'no-unused-expressions': 2, //禁止无用的表达式
            'no-unused-vars': [2, { 'vars': 'all', 'args': 'after-used' }], //不能有声明后未被使用的变量或参数
            'no-use-before-define': 2, //未定义前不能使用
            'no-useless-call': 2, //禁止不必要的call和apply
            'no-void': 2, //禁用void操作符
            'no-var': 0, //禁用var,用let和const代替
            'no-warning-comments': [
                1,
                { 'terms': ['todo', 'fixme', 'xxx'], 'location': 'start' }
            ], //不能有警告备注
            'no-with': 2, //禁用with
    
            'array-bracket-spacing': [2, 'never'], //是否允许非空数组里面有多余的空格
            'arrow-parens': 0, //箭头函数用小括号括起来
            'arrow-spacing': 0, //=>的前/后括号
            'accessor-pairs': 0, //在对象中使用getter/setter
            'block-scoped-var': 0, //块语句中使用var
            'brace-style': [1, '1tbs'], //大括号风格
            'callback-return': 1, //避免多次调用回调什么的
            'camelcase': 2, //强制驼峰法命名
            'comma-dangle': [2, 'never'], //对象字面量项尾不能有逗号
            'comma-spacing': 0, //逗号前后的空格
            'comma-style': [2, 'last'], //逗号风格,换行时在行首还是行尾
            'complexity': [0, 11], //循环复杂度
            'computed-property-spacing': [0, 'never'], //是否允许计算后的键名什么的
            'consistent-return': 0, //return 后面是否允许省略
            'consistent-this': [2, 'that'], //this别名
            'constructor-super': 0, //非派生类不能调用super,派生类必须调用super
            'curly': [2, 'all'], //必须使用 if(){} 中的{}
            'default-case': 2, //switch语句最后必须有default
            'dot-location': 0, //对象访问符的位置,换行的时候在行首还是行尾
            'dot-notation': [0, { 'allowKeywords': true }], //避免不必要的方括号
            'eol-last': 0, //文件以单一的换行符结束
            'eqeqeq': 2, //必须使用全等
            'func-names': 0, //函数表达式必须有名字
            'func-style': [0, 'declaration'], //函数风格,规定只能使用函数声明/函数表达式
            'generator-star-spacing': 0, //生成器函数*的前后空格
            'guard-for-in': 0, //for in循环要用if语句过滤
            'handle-callback-err': 0, //nodejs 处理错误
            'id-length': 0, //变量名长度
            'indent': [2, 4], //缩进风格
            'init-declarations': 0, //声明时必须赋初值
            'key-spacing': [0, { 'beforeColon': false, 'afterColon': true }], //对象字面量中冒号的前后空格
            'lines-around-comment': 0, //行前/行后备注
            'max-depth': [0, 4], //嵌套块深度
            'max-len': [0, 80, 4], //字符串最大长度
            'max-nested-callbacks': [0, 2], //回调嵌套深度
            'max-params': [0, 3], //函数最多只能有3个参数
            'max-statements': [0, 10], //函数内最多有几个声明
            'new-cap': 2, //函数名首行大写必须使用new方式调用,首行小写必须用不带new方式调用
            'new-parens': 2, //new时必须加小括号
            'newline-after-var': 2, //变量声明后是否需要空一行
            'object-curly-spacing': [0, 'never'], //大括号内是否允许不必要的空格
            'object-shorthand': 0, //强制对象字面量缩写语法
            'one-var': 1, //连续声明
            'operator-assignment': [0, 'always'], //赋值运算符 += -=什么的
            'operator-linebreak': [2, 'after'], //换行时运算符在行尾还是行首
            'padded-blocks': 0, //块语句内行首行尾是否要空行
            'prefer-const': 0, //首选const
            'prefer-spread': 0, //首选展开运算
            'prefer-reflect': 0, //首选Reflect的方法
            'quotes': [1, 'single'], //引号类型 `` "" ''
            'quote-props': [2, 'always'], //对象字面量中的属性名是否强制双引号
            'radix': 2, //parseInt必须指定第二个参数
            'id-match': 0, //命名检测
            'require-yield': 0, //生成器函数必须有yield
            'semi': [2, 'always'], //语句强制分号结尾
            'semi-spacing': [0, { 'before': false, 'after': true }], //分号前后空格
            'sort-vars': 0, //变量声明时排序
            'space-after-keywords': [0, 'always'], //关键字后面是否要空一格
            'space-before-blocks': [0, 'always'], //不以新行开始的块{前面要不要有空格
            'space-before-function-paren': [0, 'always'], //函数定义时括号前面要不要有空格
            'space-in-parens': [0, 'never'], //小括号里面要不要有空格
            'space-infix-ops': 0, //中缀操作符周围要不要有空格
            'space-return-throw-case': 0, //return throw case后面要不要加空格
            'space-unary-ops': [0, { 'words': true, 'nonwords': false }], //一元运算符的前/后要不要加空格
            'spaced-comment': 0, //注释风格要不要有空格什么的
            'strict': 2, //使用严格模式
            'use-isnan': 2, //禁止比较时使用NaN,只能用isNaN()
            'valid-jsdoc': 0, //jsdoc规则
            'valid-typeof': 2, //必须使用合法的typeof的值
            'vars-on-top': 2, //var必须放在作用域顶部
            'wrap-iife': [2, 'inside'], //立即执行函数表达式的小括号风格
            'wrap-regex': 0, //正则表达式字面量用小括号包起来
            'yoda': [2, 'never'] //禁止尤达条件
        }
    

(十三) 队列和栈(了解)

队列和栈都是后台语言的数据结构, js可以通过数组的一些方法进行模拟队列和栈

  1. 定义: 栈是一种后进先出的数据结构,也就是说最新添加(后进)的项最早被移出;它是一种运算受限的线性表,只能在表头/栈顶进行插入和删除操作。栈有栈底和栈顶。

  2. 入栈和出栈: 向一个栈插入新元素叫入栈(进栈),就是把新元素放入到栈顶的上面,成为新的栈顶;从一个栈删除元素叫出栈,就是把栈顶的元素删除掉,相邻的成为新栈顶;

  3. js数组方法模拟栈的数据结构

    1. 入栈 push()
    2. 出栈 pop()
    // 用数组方法模拟栈
    var arr = [];
    // 模拟入栈
    arr.push('a');
    arr.push('b');
    arr.push('c');
    
    
    // 模拟出栈
    console.log(arr.pop());
    console.log(arr.pop());
    console.log(arr.pop());
    

队列

  1. 定义: 队列是一种先进先出的数据结构。 队列在列表的末端增加项,在首端移除项。

  2. 出队列和入队列:队列允许在表的首端(队列头)进行删除操作,在表的末端(队列尾)进行插入操作;(例如在银行窗口排队办理业务,最前面的第一个人开始办理,后面来的人只能在队伍末尾排队,直到排到他们为止),队列是实现多任务的重要机制!

  3. js数组模拟入队列和出队列

    • 入队列 unshift()
    • 出队列 pop()
    // 用数组方法模拟栈
    var arr = [];
    // 模拟入队列
    arr.unshift('a');
    arr.unshift('b');
    arr.unshift('c');
    
    
    // 模拟出队列
    console.log(arr.pop());
    console.log(arr.pop());
    console.log(arr.pop());
    

算法

参考链接: https://www.cnblogs.com/AlbertP/p/10847627.html

img

<script>
    function bubbleSort(arr) {
    console.log(arr);
    var len = arr.length;
    for (var i = 0; i < len; i++) {
        // i循环一次,j循环len - 1 - i次
        for (var j = 0; j < len - 1 - i; j++) {
            if (arr[j] > arr[j + 1]) { //相邻元素两两对比
                var temp = arr[j + 1]; //元素交换
                arr[j + 1] = arr[j];
                arr[j] = temp;
            }
        }
        console.log(i,arr);
    }
    return arr;
}


var arr = [8,2,9,1,7,3,6,4,5];
var newArr = bubbleSort(arr); 
</script> 

img

排序演示代码

<!DOCTYPE html>
<head>
    <meta charset="UTF-8">
    <style>
        .box { margin-top: 50px; border-bottom: 1px solid; display: flex; align-items: flex-end; width: 500px;}
        p { width: 40px; text-align: center; display: flex; align-items: center; justify-content: center; }
        p+p {margin-left: 20px;}
        .p1 { background-color: gray; height: 160px; }
        .p2 { background-color: gold; height: 40px; }
        .p3 { background-color: green; height: 180px; }
        .p4 { background-color: blue; height: 20px; }
        .p5 { background-color: pink; height: 140px; }
        .p6 { background-color: salmon; height: 60px; }
        .p7 { background-color: rgb(49, 199, 86); height: 120px; }
        .p8 { background-color: rgb(250, 114, 155); height: 80px; }
        .p9 { background-color: rgb(98, 62, 228); height: 100px; }
    </style>
</head>

<body>
    <div class="box">
        <p class="p1">8</p>
        <p class="p2">2</p>
        <p class="p3">9</p>
        <p class="p4">1</p>
        <p class="p5">7</p>
        <p class="p6">3</p>
        <p class="p7">6</p>
        <p class="p8">4</p>
        <p class="p9">5</p>
    </div>
</body> 
</html>

作业:

  1. 完成每日生鲜搜索功能

  2. 说出以下输出结果, 并解释为什么

    function fun() {
        var r = new Array();
        for(var i=0;i<3;i++) {
            r[i] = function() {
                return i;
            }
        }
        return r;
    }
    var fun2 = fun();
    for(var i=0;i<fun2.length;i++) {
        console.log(fun2[i]()); 
    }
    
  3. 说出以下代码运行结果, 并解释为什么

    var name = 'xiao ming';
    var obj = {
      name: 'xiao hong',
      getName: function() {
        return function() {
          return this.name;
        }
      }
    } 
    console.log(obj.getName()());
    
  4. 说出以下代码运行结果, 并解释为什么

    var param = 1;
    function main() { 
      console.log('1',param);
      var param = 2;
      console.log('2',this.param);
      this.param = 3;
    }
    
    main(); 
    var m = new main();